package com.vote.admin.service;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.vote.common.constant.RedisConstants;
import com.vote.common.entity.VoteOption;
import com.vote.common.entity.VoteProject;
import com.vote.common.entity.VoteRecord;
import com.vote.common.entity.VoteUser;
import com.vote.common.enums.UserSessionEnum;
import com.vote.common.enums.VoteTypeEnum;
import com.vote.common.returns.RespCode;
import com.vote.common.returns.RespEntity;
import com.vote.common.service.VoteOptionService;
import com.vote.common.service.VoteProjectService;
import com.vote.common.service.VoteRecordService;
import com.vote.common.service.VoteUserService;
import com.vote.common.utils.IPUtil;
import com.vote.common.utils.RedisUtil;
import com.vote.common.utils.TimeUtilCustomize;
import com.vote.admin.vo.record.OptionRecord;
import com.vote.admin.vo.result.UserResult;
import com.vote.admin.vo.result.VoteDetailResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class VoteUserDetailService {

    @Autowired
    private VoteProjectService voteProjectService;

    @Autowired
    private VoteUserService voteUserService;

    @Autowired
    private VoteOptionService voteOptionService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private VoteRecordService voteRecordService;

    /**
     * 投票活动详情
     * @param id
     * @return
     */
    public RespEntity<VoteDetailResult<List<OptionRecord>>> getVoteProjectInfo(Integer id) {

        //投票活动详情
        VoteProject voteProject = voteProjectService.getById(id);

        if (Objects.isNull(voteProject)) {
            log.info("该id对应的活动详情不存在, id={}", id);
            return new RespEntity<>(RespCode.SUCCESS);
        }

        //投票活动发起用户
        VoteUser voteUser = voteUserService.getById(voteProject.getUserId());

        //投票选项集合
        QueryWrapper<VoteOption> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(VoteOption::getProjectId, id)
                .eq(VoteOption::getDeleted, 0);
        List<VoteOption> voteOptionList = voteOptionService.list(queryWrapper);

        List<OptionRecord> optionRecordList = Lists.newArrayList();
        if (CollectionUtil.isNotEmpty(voteOptionList)) {
            //总票数
            Integer total = voteOptionList.stream()
                    .map(VoteOption::getOptionGrade)
                    .reduce((v1, v2) -> v1 + v2)
                    .get();

            voteOptionList.forEach(voteOption -> {
                String optionGrade = null;
                //计数制展示
                if (0 == voteProject.getProjectShow()) {
                    optionGrade = voteOption.getOptionGrade() + "票";
                }
                //百分比展示
                if (1 == voteProject.getProjectShow()) {
                    BigDecimal percent = new BigDecimal(0);

                    if (! total.equals(0)) {
                        //占比
                        percent = new BigDecimal(voteOption.getOptionGrade())
                                .multiply(new BigDecimal(100))
                                .divide(new BigDecimal(total), 2, BigDecimal.ROUND_HALF_UP);
                    }
                    optionGrade = percent + "%";

                }

                OptionRecord optionRecord = OptionRecord.builder()
                        .id(voteOption.getId())
                        .optionName(voteOption.getOptionName())
                        .optionImg(voteOption.getOptionImg())
                        .optionGrade(optionGrade)
                        .build();

                optionRecordList.add(optionRecord);
            });
        }

        //数据组装
        VoteDetailResult voteDetailResult = VoteDetailResult.builder()
                .id(voteProject.getId())
                .publishName(voteUser.getName())
                .projectName(voteProject.getProjectName())
                .projectChoice(voteProject.getProjectChoice())
                .projectRule(voteProject.getProjectRule())
                .projectImg(voteProject.getProjectImg())
                .projectDesc(voteProject.getProjectDesc())
                .projectStart(voteProject.getProjectStart())
                .projectEnd(voteProject.getProjectEnd())
                .records(optionRecordList)
                .build();

        //更新活动浏览量数据
        updateProjectView(voteProject);


        return new RespEntity<>(RespCode.SUCCESS, voteDetailResult);

    }

    /**
     * 更新活动浏览量数据
     * @param voteProject
     */
    private void updateProjectView(VoteProject voteProject) {
        Integer projectView = voteProject.getProjectView();
        projectView += 1;
        voteProject.setProjectView(projectView);
        voteProjectService.updateById(voteProject);
    }

    /**
     * 投票
     * @param id
     * @return
     */
    public RespEntity<String> vote(Integer id, HttpServletRequest request) {
        VoteOption voteOption = voteOptionService.getById(id);
        VoteProject voteProject = voteProjectService.getById(voteOption.getProjectId());
        //投票类型
        Integer projectPublic = voteProject.getProjectPublic();
        //投票次数
        Integer projectRule = voteProject.getProjectRule();
        //投票时间
        String recordTime = TimeUtilCustomize.parseDateToStr(TimeUtilCustomize.DATE_TIME_FORMAT, new Date());


        //匿名投票
        if (0 == projectPublic) {
            //IP地址
            String ip = IPUtil.getIpAddress(request);
            //今日投票记录
            String value = redisUtil.hget(RedisConstants.Key.getVoteUserIp(ip), voteProject.getId() + "");
            value = StringUtils.isBlank(value) ? "0:0" : value;
            //今日投票次数
            int count = Integer.parseInt(value.split(":")[0]);
            log.info("ip地址为：{}的用户，今日为活动{}，共投票{}次", ip, voteProject.getId(), count);

            //单选
            if (0 == voteProject.getProjectChoice()) {
                String str = value.split(":")[1];
                if (! StringUtils.equals("0", str)) {
                    String[] optionIds = str.split("/");
                    if (Objects.nonNull(optionIds) && 0 != optionIds.length) {
                        String optionId = optionIds[optionIds.length - 1];

                        //单选只能给同一个选项投票
                        if (! StringUtils.equals(optionId, id + "")) {
                            return new RespEntity(RespCode.NOT_SINGLE);
                        }
                    }
                }
            }


            //达到投票次数上限
            if (count >= projectRule) {
                return new RespEntity(RespCode.VOTED);
            }

            //今日投票次数加1，并设置今天凌晨过期
            count += 1;
            value = count + ":" + value.split(":")[1] + "/" + voteOption.getId();
            redisUtil.hset(RedisConstants.Key.getVoteUserIp(ip), voteProject.getId() + "", value + "", TimeUtilCustomize.getSecondsTodayEarlyMorning());

            //更新数据库投票次数和浏览量
            updateVote(voteOption, voteProject);

            //投票记录 数据组装
            VoteRecord voteRecord = VoteRecord.builder()
                    .projectId(voteProject.getId())
                    .optionId(voteOption.getId())
                    .userIp(ip)
                    .voteType(VoteTypeEnum.ANONYMOUS.getCode())
                    .recordTime(recordTime)
                    .build();

            //投票记录 异步接入
            voteRecordService.accessRecord(voteRecord);

            //更新页面展示
            String optionGrade = updateView(id);

            log.info("IP地址为：{}的用户匿名投票成功，剩余投票次数为：{}次", ip, (projectRule-count));
            return new RespEntity(RespCode.SUCCESS, optionGrade);
        }


        //实名投票
        if (1 == projectPublic) {
            //用户id
            UserResult voteUser = (UserResult) request.getSession().getAttribute(UserSessionEnum.USER.getCode());

            //实名投票需要先登录
            if (Objects.isNull(voteUser)) {
                return new RespEntity(RespCode.FIRST_LOGIN);
            }

            //今日投票记录
            String value = redisUtil.hget(RedisConstants.Key.getVoteUserId(voteUser.getId()), voteProject.getId() + "");
            value = StringUtils.isBlank(value) ? "0:0" : value;
            //今日投票次数
            int count = Integer.parseInt(value.split(":")[0]);
            log.info("userId为：{}的用户，今日为活动{}，共投票{}次", voteUser.getId(), voteProject.getId(), count);

            //单选
            if (0 == voteProject.getProjectChoice()) {
                String str = value.split(":")[1];
                if (! StringUtils.equals("0", str)) {
                    String[] optionIds = str.split("/");
                    if (Objects.nonNull(optionIds) && 0 != optionIds.length) {
                        String optionId = optionIds[optionIds.length - 1];

                        //单选只能给同一个选项投票
                        if (! StringUtils.equals(optionId, id + "")) {
                            return new RespEntity(RespCode.NOT_SINGLE);
                        }
                    }
                }
            }

            //达到投票次数上限
            if (count >= projectRule) {
                return new RespEntity(RespCode.VOTED);
            }

            //投票次数加1，并设置今天凌晨过期
            count += 1;
            value = count + ":" + value.split(":")[1] + "/" + voteOption.getId();
            redisUtil.hset(RedisConstants.Key.getVoteUserId(voteUser.getId()), voteProject.getId() + "", value + "", TimeUtilCustomize.getSecondsTodayEarlyMorning());

            //更新数据库投票次数和浏览量
            updateVote(voteOption, voteProject);

            //投票记录 数据组装
            VoteRecord voteRecord = VoteRecord.builder()
                    .projectId(voteProject.getId())
                    .optionId(voteOption.getId())
                    .userId(voteUser.getId())
                    .voteType(VoteTypeEnum.ANONYMOUS.getCode())
                    .recordTime(recordTime)
                    .build();

            //投票记录 异步接入
            voteRecordService.accessRecord(voteRecord);

            log.info("userId为：{}的用户实名投票成功，剩余投票次数为：{}次", voteUser.getId(), (projectRule-count));
        }

        //更新页面展示
        String optionGrade = updateView(id);

        return new RespEntity(RespCode.SUCCESS, optionGrade);
    }

    /**
     * 更新页面展示
     * @param id
     * @return
     */
    private String updateView(Integer id) {
        VoteOption voteOption = voteOptionService.getById(id);
        VoteProject voteProject = voteProjectService.getById(voteOption.getProjectId());
        //更新页面数据
        String optionGrade = null;
        //计数制展示
        if (0 == voteProject.getProjectShow()) {
            optionGrade = voteOption.getOptionGrade() + "票";
        }

        //百分比展示
        if (1 == voteProject.getProjectShow()) {
            BigDecimal percent = new BigDecimal(voteOption.getOptionGrade())
                    .multiply(new BigDecimal(100))
                    .divide(new BigDecimal(voteProject.getProjectVote()), 2, BigDecimal.ROUND_HALF_UP);
            optionGrade = percent + "%";
        }
        return optionGrade;
    }

    /**
     * 更新数据库投票次数和浏览量
     * @param voteOption
     * @param voteProject
     */
    private void updateVote(VoteOption voteOption, VoteProject voteProject) {
        //更新选项表投票次数和浏览量
        Integer optionGrade = voteOption.getOptionGrade();
        Integer optionView = voteOption.getOptionView();

        optionGrade += 1;
        optionView += 1;

        voteOption.setOptionGrade(optionGrade);
        voteOption.setOptionView(optionView);

        voteOptionService.updateById(voteOption);

        //更新活动表投票次数和浏览量
        Integer projectVote = voteProject.getProjectVote();
        Integer projectView = voteProject.getProjectView();

        projectVote += 1;
        projectView += 1;

        voteProject.setProjectVote(projectVote);
        voteProject.setProjectView(projectView);

        voteProjectService.updateById(voteProject);
    }

}

